package org.xtremeobjects.domain.states;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.xtremeobjects.domain.states.Event;
import org.xtremeobjects.domain.states.State;
import org.xtremeobjects.util.collections.CollectionSelector;

/**
 * @author    Juan
 */
public class StateMachine {
	
	public State ANY = new State(this, "Any"); 

	StateMachine(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public List<Event> getEvents() {
		return Collections.unmodifiableList(events);
	}

	public List<State> getStates() {
		return Collections.unmodifiableList(states);
	}

	public List<Transition> getTransitions() {
		return Collections.unmodifiableList(transitions);
	}

	public State state(String name) {
		State state = findStateByName(name);
		if(state==null)
			state = createState(name);
		return state;
	}

	public State start() {
		if(start==null)
			 start = state("Start");
		return start;
	}
	

	public State end() {
		if(end==null)
			 end = state("End");
		return end;
	}

	public State findStateByName(String name) {
		return findStateByName(name, states);
	}

	private State findStateByName(String name, List<State> states) {
		if(name.equals("Any"))
			return ANY;
		for (State state : states) {
			if(state.getId().equals(name))
				return state;
			if(state.getStates()!=null) {
				State subState = findStateByName(name, state.getStates());
				if(subState!=null)
					return subState;
			}
				
		}
		return null;
	}

	public Event findEventByName(String name) {
		for (Event event : events) {
			if(event.getId().equals(name))
				return event;
		}
		return null;
	}

	public List<Transition> findTransition(String from, String to) {
		State fromState = findStateByName(from);
		State toState = findStateByName(to);
		if(fromState==null)
			throw new RuntimeException("Invalid state name from " + from);
		if(toState==null)
			throw new RuntimeException("Invalid state name to " + to);
		
		return findTransition( fromState, toState);
	}

	public List<Transition> findTransition(State from, State to) {
		ArrayList<Transition> foundTransitions = new ArrayList<Transition>();
		for (Transition transition : transitions) {
			if(transition.getFrom().equals(from) && transition.getTo().equals(to))
				foundTransitions.add(transition);
		}
		// Look in superstates
		if(from.getSuperState()!=null)
			foundTransitions.addAll(findTransition(from.getSuperState(), to));
		return foundTransitions;
	}
	
	public List<Transition> getTransitionsFrom(State fromState) {
		
        CollectionSelector stateSelector = new CollectionSelector()
        {
            public boolean selectBlock(Object listElement, Object keyValue)
            {
                return ((Transition)listElement).getFrom().equals(keyValue);
            }
        };
        return (List<Transition>)stateSelector.select(getTransitions(), fromState);
	}

	private State createState(String name) {
		State state = new State( this, name );
		addState(state);
		return state;
	}

	Event createEvent(String name) {
		Event ev = new Event( this, name );
		addEvent(ev);
		return ev;
	}

	Transition createTransition(State from, State to) {
		Transition transition = new Transition(this, from, to);
		addTransition(transition);
		return transition;
	}
	
	private void addState(State state) {
		states.add(state);
	}

	private void addTransition(Transition transition) {
		transitions.add(transition);
	}

	private void addEvent(Event ev) {
		events.add(ev);
	}

	public State start;
	public State end;
	
	private String name;
	List<State> states = new ArrayList<State>();
	List<Transition> transitions = new ArrayList<Transition>();
	List<Event> events = new ArrayList<Event>();
}
